package lib;


import java.util.ArrayList;
import java.util.HashMap;

import interfaces.Cell;
import interfaces.Display;
import interfaces.World;

/**
* The concrete World implementation. An instance of this class is returned
* to the user by the WorldFactory. The grid is represented by a square 2D
* array, or a 2D array with second dimension equal to 1 if the user desires
* a 1D automaton.
*/
public class WorldImpl implements World {
	Cell[][] grid;						/* array of cells */
	int dimension;						/* dimension of automaton */
    boolean two_dim;					/* flag signifying 2D or 1D automaton */
    HashMap<String, Display> displayMap = new HashMap<String,Display>();
    /**
     * Given a Location, returns the cell stored at that location in the grid
     */
	@Override
	public Cell getCell(Location loc) {
		int X = loc.getX();
		int Y = loc.getY();
		if((X < dimension && X >= 0) && 
				(((Y == 0) && !two_dim) ||
				(Y < dimension && Y >= 0))){
			return grid[X][Y];
		}
		else
			return null;
	}

	/**
	 * Returns the dimension of the grid
	 */
	@Override
	public int getDimension() {
		return dimension;
	}

	/**
	 * Sets a given location to a cell pass as an argument 
	 */
	@Override
	public void setCell(Cell cell, Location loc) {
		grid[loc.getX()][loc.getY()] = cell;
	}

	/**
	 * Sets every location in the grid to be a certain type of cell.
	 * Useful for initialization of the grid
	 * @param cell type of cell to be placed into every location
	 */
	public void setAllCells(Cell cell){
		for(int i = 0; i < dimension; i++){
			for(int j = 0; j < dimension; j++){
				grid[i][j] = cell;
			}
		}
	}
	
	/**
	 * Given an ArrayList of locations and a cell, sets the valid
	 * location in the grid to that cell. Invalid locations are ignored
	 * (no exception thrown) 
	 * @param locs ArrayList of Location instances
	 * @param cell cell to be put into valid locations
	 */
	public void setCellsFromArrayList(ArrayList<Location> locs, Cell cell){
		for(int i = 0; i < locs.size(); i++){
			int X = locs.get(i).getX();
			int Y = locs.get(i).getY();
			if((0 <= X && X < dimension) && 
					((Y == 0 && !two_dim) || 
					(0 <= Y && Y < dimension))){
				grid[X][Y] = cell;
			}
		}
	}
	
	/**
	 * The constructor
	 * @param dim dimension of the grid to be displayed
	 * @param two_dim flag which is true for 2D arrays, false o/w
	 */
	public WorldImpl(int dim, boolean two_dim){
		this.two_dim = two_dim;
		dimension = dim;
		if(two_dim){
			grid = new Cell[dim][dim];
		}else{
			grid = new Cell[dim][1];
		}
			
	}

	/**
	 * Returns a boolean which is true if the world is 2D,
	 * false otherwise.
	 */
	public boolean is2D(){
		return two_dim;
	}

	/**
	 * run updates every single Cell in the grid according to the rules
	 * of Cell.transform for each type of Cell. It essentially does
	 * 1 step of the automaton. 
	 */
	@Override
	public void run() {
			Cell[][] new_grid;
			if(two_dim){
				new_grid = new Cell[dimension][dimension];
				for(int i=0;i<dimension;i++){
					for(int j=0;j<dimension;j++){
						new_grid[i][j] = this.grid[i][j].transform(this, new Location(i,j));
					}
				}
			}
			else{
				new_grid = new Cell[dimension][1];
				for(int i=0;i<dimension;i++){
						new_grid[i][0] = this.grid[i][0].transform(this, new Location(i,0));
				}
			}
			this.grid = new_grid;
		}

	/**
	 * Puts a (string, Display) pair into the HashMap
	 * This method is used by user to initialize World
	 */
	@Override
	public void put(String s,Display d ) {
		this.displayMap.put(s, d);
	}

	/**
	 * Returns the value of the given String in the HashMap
	 * This method is used by WorldUI to visualize the cells in
	 * World
	 */
	@Override
	public Display get(String s) {
		return this.displayMap.get(s);
	}
}
